/**
 * OSAmI: Open Source Ambient Intelligence
 * Servlets running on the gateway, for displaying data from the sensor nodes.
 * Copyright (C) Jonathan Ferreira 2010 (internship in LCIS-INPG, for a demo in the FR part of the OSAMI project)
 * Contact: osami-general@osami-commons.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA
 */
package org.osami.gateway.display.internal.profile;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.felix.ipojo.annotations.Bind;
import org.apache.felix.ipojo.annotations.Component;
import org.apache.felix.ipojo.annotations.Instantiate;
import org.apache.felix.ipojo.annotations.Invalidate;
import org.apache.felix.ipojo.annotations.Requires;
import org.apache.felix.ipojo.annotations.Unbind;
import org.apache.felix.ipojo.annotations.Validate;
import org.osami.gateway.display.internal.ServletPaths;
import org.osami.gateway.profile.IProfileService;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
import org.osgi.service.http.HttpContext;
import org.osgi.service.http.HttpService;
import org.osgi.service.http.NamespaceException;

@Component
@Instantiate
public class ProfileServiceServlet extends HttpServlet {

    public static final String PARAMETER_VALUE = "parameter-value";
    public static final String SERVICE_ID = "service-id";
    public static final String PARAMETER_NAME = "parameter-name";

    private static final long serialVersionUID = 1L;

    @Requires
    private HttpContext context;

    @Requires
    private HttpService httpService;

    private Map<ServiceReference, IProfileService> profiles = new HashMap<ServiceReference, IProfileService>();

    @Validate
    public void start() throws ServletException, NamespaceException {
        this.httpService.registerServlet(ServletPaths.PROFILE, this, null, this.context);
    }

    @Invalidate
    public void stop() {
        this.httpService.unregister(ServletPaths.PROFILE);
    }

    @Bind(aggregate = true)
    public void bindProfile(IProfileService profile, ServiceReference reference) {
        this.profiles.put(reference, profile);
    }

    @Unbind
    public void unbindProfile(ServiceReference reference) {
        this.profiles.remove(reference);
    }

    @Override
    synchronized public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        PrintWriter writer = response.getWriter();

        writer.println("<?xml version='1.0'?>");
        writer.println("<html xmlns='http://www.w3.org/1999/xhtml' lang='en'>");

        // HTML Head
        // ---------------------------------------------------
        writer.println("<head>");
        writer.println("  <title>ProfileService(s) Configuration</title>");
        writer.println("  <link href='/resource/css/designSensor.css' rel='stylesheet' type='text/css' />");
        writer.println("</head>");

        // HTML Body
        // ---------------------------------------------------
        writer.println("<body>");

        // Update the Profile if possible
        // ---------------------------------------------------
        String parameterValue = request.getParameter(PARAMETER_VALUE);
        String serviceId = request.getParameter(SERVICE_ID);
        String parameterName = request.getParameter(PARAMETER_NAME);
        if (serviceId != null) {
            Long id = Long.valueOf(serviceId);
            updateProfileConfiguration(id, parameterName, parameterValue);
        }


        // Print title
        // ---------------------------------------------------
        writer.println("<div style='font-color:grey;text-align:center'>");
        writer.println("  <h1>Profiles Services</h1>");
        writer.println("</div>");

        // Print each profile service description
        // ---------------------------------------------------
        for (Map.Entry<ServiceReference, IProfileService> entry : this.profiles.entrySet()) {
            printProfile(writer, entry.getKey(), entry.getValue());
        }

        // Horizontal rule
        writer.println("<hr/>");
        writer.println("<a href='/'>Back to index ...</a>");

        writer.println("</body>");
        writer.println("</html>");
    }

    private void updateProfileConfiguration(Long serviceId, String parameterName, String parameterValue) {
        IProfileService service = findProfile(serviceId);
        if (service != null) {
            service.setParameter(parameterName, parameterValue);
        }
    }

    private IProfileService findProfile(Long serviceId) {
        for (Map.Entry<ServiceReference, IProfileService> entry : this.profiles.entrySet()) {
            ServiceReference ref = entry.getKey();
            if (serviceId.equals(ref.getProperty(Constants.SERVICE_ID))) {
                return entry.getValue();
            }
        }
        return null;
    }

    private void printProfile(final PrintWriter writer,
                              final ServiceReference reference,
                              final IProfileService service) {

        // Print a title
        printProfileInfo(writer, reference);

        // start table
        writer.println("<table width='100%' cellpadding='0'>");

        // headers
        writer.println("<tr>");
        writer.println("<th width='30%'>Name</th>");
        writer.println("<th>Value</th>");
        writer.println("</tr>");

        // body
        for (String name : service.getParameters()) {

            writer.println("<tr>");

            // Name in the first cell
            writer.println("<td>");
            writer.println(name);
            writer.println("</td>");

            // Value in the second cell
            writer.println("<td>");
            String value = service.getParameter(name);
            printUpdateValueForm(writer, reference, name, value);
            writer.println("</td>");

            writer.println("</tr>");
        }

        // end
        writer.println("</table>");

        writer.println("<br />");
    }

    private void printProfileInfo(PrintWriter writer, ServiceReference reference) {
        Long serviceId = (Long) reference.getProperty(Constants.SERVICE_ID);
        String[] interfaces = (String[]) reference.getProperty(Constants.OBJECTCLASS);
        writer.println("<h2>" + Arrays.toString(interfaces) + " (" + serviceId + ")</h2>");

        writer.println("<div style='font-color:grey;font-style:italic;text-align:center'>");
        String[] keys = reference.getPropertyKeys();
        for (String key : keys) {
            writer.println("<b>" + key + "</b>");
            writer.println(":");
            Object value = reference.getProperty(key);
            if (value.getClass().isArray()) {
                value = Arrays.toString((Object[]) value);
            }
            writer.println(value);
            writer.println("<br />");
        }
        writer.println("</div>");

    }

    private void printUpdateValueForm(final PrintWriter writer,
                                      final ServiceReference reference,
                                      final String name,
                                      final String value) {
        writer.println("<form method='get' action='" + ServletPaths.PROFILE + "'>");
        writer.println("  <input type='text' name='" + PARAMETER_VALUE + "' size='50' value='" + value + "' />");
        writer.println("  <input type='submit' value='Update' />");
        writer.println("  <input type='hidden' name='" + PARAMETER_NAME + "' value='" + name + "' />");
        writer.println("  <input type='hidden' name='" + SERVICE_ID + "' value='" + reference.getProperty(Constants.SERVICE_ID) + "' />");
        writer.println("</form>");
    }

}
